package main

import (
	"fmt"
	"math/rand/v2"
	"os"
	"time"

	"github.com/nsf/termbox-go"
)

const (
	width  = 30
	height = 30
)

type point struct {
	x, y int
}

type snake struct {
	body  []point // record body
	dir   point
	grow  bool
	score int
}

func (s *snake) move() {
	head := s.body[0]
	newHead := point{x: head.x + s.dir.x, y: head.y + s.dir.y}

	// Check if the snake collides with walls or itself
	if newHead.x < 0 || newHead.y < 0 || newHead.x >= width || newHead.y >= height || s.collidesWithBody(newHead) {
		gameOver(s.score)
	}

	s.body = append([]point{newHead}, s.body...)
	if !s.grow {
		s.body = s.body[:len(s.body)-1]
	} else {
		s.grow = false
	}
}

func (s *snake) collidesWithBody(p point) bool {
	for _, part := range s.body[1:] {
		if part == p {
			return true
		}
	}
	return false
}

func (s *snake) eatApple(apple point) bool {
	return s.body[0] == apple
}

func (s *snake) draw() {
	for _, p := range s.body {
		termbox.SetCell(p.x, p.y, '■', termbox.ColorGreen, termbox.ColorBlack)
	}
}

func newApple() point {
	i := rand.IntN(5) + 1
	return point{x: width / i, y: height / i}
}

func drawApple(apple point) {
	termbox.SetCell(apple.x, apple.y, '■', termbox.ColorRed, termbox.ColorBlack)
}

func gameOver(score int) {
	termbox.Clear(termbox.ColorBlack, termbox.ColorBlack)
	msg := fmt.Sprintf("Game Over! Your score is: %d", score)
	for i, c := range msg {
		termbox.SetCell(i+width/4, height/2, c, termbox.ColorWhite, termbox.ColorBlack)
	}
	termbox.Flush()
	time.Sleep(time.Second * 1)
	os.Exit(0)
}

func main() {
	err := termbox.Init()
	if err != nil {
		fmt.Println("Failed to initialize termbox:", err)
		os.Exit(1)
	}
	defer termbox.Close()

	s := snake{
		body: []point{{x: 5, y: 5}},
		dir:  point{x: 1, y: 0},
	}

	apple := newApple()
	for {
		termbox.Clear(termbox.ColorBlack, termbox.ColorBlack)

		// Handle user input
		event := termbox.PollEvent()
		switch event.Type {
		case termbox.EventKey:
			switch event.Key {
			case termbox.KeyEsc:
				gameOver(s.score)
			case termbox.KeyArrowUp:
				if s.dir.y == 0 {
					s.dir = point{x: 0, y: -1}
				}
			case termbox.KeyArrowDown:
				if s.dir.y == 0 {
					s.dir = point{x: 0, y: 1}
				}
			case termbox.KeyArrowLeft:
				if s.dir.x == 0 {
					s.dir = point{x: -1, y: 0}
				}
			case termbox.KeyArrowRight:
				if s.dir.x == 0 {
					s.dir = point{x: 1, y: 0}
				}

			default:

			}
		default:

		}

		// Move snake and check if it eats the apple
		s.move()
		if s.eatApple(apple) {
			s.grow = true
			s.score++
			apple = newApple() // regenerate apple at a new position
		}

		// Draw the game objects
		s.draw()
		drawApple(apple)

		// Display the score
		scoreStr := fmt.Sprintf("Score: %d", s.score)
		for i, c := range scoreStr {
			termbox.SetCell(i, 0, c, termbox.ColorWhite, termbox.ColorBlack)
		}

		// Flush the screen to show the updates
		termbox.Flush()

		// Control the speed of the game
		time.Sleep(10 * time.Millisecond)
	}
}
